// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 包modget实现模块感知的``go-get''命令。
package modget

// “go get”的参数是带有可选版本查询的模式，带有
// 版本查询默认为“升级”。
// None
// 这些模式通常被解释为包模式。然而，如果
// 模式与包不匹配，它被解释为*模块*
// 图案对于依赖于
// 模块（或包含软件包的模块）的选定版本，
// 模式是否表示包或模块可能会随着更新而改变
// 应用（参见mod_get_patchmod.txt中的示例）。
// None
// 还有一些其他模棱两可的情况需要解决。包可以存在于中
// 同一版本的两个不同模块：例如，包
// example.com/foo可以在module example.com和module中找到
// 这些模块可能有独立的v0.1.0标记-因此
// 输入'example.com/foo@v0.1.0“可以在语法上指的是
// 从任一模块加载的包！（请参阅mod_get_buzzalize_pkg.txt。）
// 如果参数不明确，用户通常可以通过指定
// 所涉及的*所有*潜在模块路径的显式版本。

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"sync"

	"cmd/go/internal/base"
	"cmd/go/internal/imports"
	"cmd/go/internal/load"
	"cmd/go/internal/modfetch"
	"cmd/go/internal/modload"
	"cmd/go/internal/par"
	"cmd/go/internal/search"
	"cmd/go/internal/work"

	"golang.org/x/mod/modfile"
	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

var CmdGet = &base.Command{
	// 注意：-d-u被明确列出，因为它们是最常见的get标志。
	// 不要发送删除它们的CLs，因为它们被[get flags]覆盖。
	UsageLine: "go get [-d] [-t] [-u] [-v] [build flags] [packages]",
	Short:     "add dependencies to current module and install them",
	Long: `
Get resolves its command-line arguments to packages at specific module versions,
updates go.mod to require those versions, downloads source code into the
module cache, then builds and installs the named packages.

To add a dependency for a package or upgrade it to its latest version:

	go get example.com/pkg

To upgrade or downgrade a package to a specific version:

	go get example.com/pkg@v1.2.3

To remove a dependency on a module and downgrade modules that require it:

	go get example.com/mod@none

See https:// golang.org/ref/mod#获取详细信息。

The 'go install' command may be used to build and install packages. When a
version is specified, 'go install' runs in module-aware mode and ignores
the go.mod file in the current directory. For example:

	go install example.com/pkg@v1.2.3
	go install example.com/pkg@latest

See 'go help install' or https:// org/ref/mod#有关详细信息，请转到install。

In addition to build flags (listed in 'go help build') 'go get' accepts the
following flags.

The -t flag instructs get to consider modules needed to build tests of
packages specified on the command line.

The -u flag instructs get to update modules providing dependencies
of packages named on the command line to use newer minor or patch
releases when available.

The -u=patch flag (not -u patch) also instructs get to update dependencies,
but changes the default to select patch releases.

When the -t and -u flags are used together, get will update
test dependencies as well.

The -d flag instructs get not to build or install packages. get will only
update go.mod and download source code needed to build packages.

Building and installing packages with get is deprecated. In a future release,
the -d flag will be enabled by default, and 'go get' will be only be used to
adjust dependencies of the current module. To install a package using
dependencies from the current module, use 'go install'. To install a package
ignoring the current module, use 'go install' with an @version suffix like
"@latest" after each argument.

For more about modules, see https:// golang.org/ref/mod。

For more about specifying packages, see 'go help packages'.

This text describes the behavior of get using modules to manage source
code and dependencies. If instead the go command is running in GOPATH
mode, the details of get's flags and effects change, as does 'go help get'.
See 'go help gopath-get'.

See also: go build, go install, go clean, go mod.
	`,
}

// 请注意，此帮助文本是一个权宜之计，可使模块意识到获取帮助文本
// 即使在非模块设置中也可用。当旧文件被删除时，应将其删除
// 已删除。这不应被视为开创了等级制度的先例
// 用破折号帮助命名。
var HelpModuleGet = &base.Command{
	UsageLine: "module-get",
	Short:     "module-aware go get",
	Long: `
The 'go get' command changes behavior depending on whether the
go command is running in module-aware mode or legacy GOPATH mode.
This help text, accessible as 'go help module-get' even in legacy GOPATH mode,
describes 'go get' as it operates in module-aware mode.

Usage: ` + CmdGet.UsageLine + `
` + CmdGet.Long,
}

var HelpVCS = &base.Command{
	UsageLine: "vcs",
	Short:     "controlling version control with GOVCS",
	Long: `
The 'go get' command can run version control commands like git
to download imported code. This functionality is critical to the decentralized
Go package ecosystem, in which code can be imported from any server,
but it is also a potential security problem, if a malicious server finds a
way to cause the invoked version control command to run unintended code.

To balance the functionality and security concerns, the 'go get' command
by default will only use git and hg to download code from public servers.
But it will use any known version control system (bzr, fossil, git, hg, svn)
to download code from private servers, defined as those hosting packages
matching the GOPRIVATE variable (see 'go help private'). The rationale behind
allowing only Git and Mercurial is that these two systems have had the most
attention to issues of being run as clients of untrusted servers. In contrast,
Bazaar, Fossil, and Subversion have primarily been used in trusted,
authenticated environments and are not as well scrutinized as attack surfaces.

The version control command restrictions only apply when using direct version
control access to download code. When downloading modules from a proxy,
'go get' uses the proxy protocol instead, which is always permitted.
By default, the 'go get' command uses the Go module mirror (proxy.golang.org)
for public packages and only falls back to version control for private
packages or when the mirror refuses to serve a public package (typically for
legal reasons). Therefore, clients can still access public code served from
Bazaar, Fossil, or Subversion repositories by default, because those downloads
use the Go module mirror, which takes on the security risk of running the
version control commands using a custom sandbox.

The GOVCS variable can be used to change the allowed version control systems
for specific packages (identified by a module or import path).
The GOVCS variable applies when building package in both module-aware mode
and GOPATH mode. When using modules, the patterns match against the module path.
When using GOPATH, the patterns match against the import path corresponding to
the root of the version control repository.

The general form of the GOVCS setting is a comma-separated list of
pattern:vcslist rules. The pattern is a glob pattern that must match
one or more leading elements of the module or import path. The vcslist
is a pipe-separated list of allowed version control commands, or "all"
to allow use of any known command, or "off" to disallow all commands.
Note that if a module matches a pattern with vcslist "off", it may still be
downloaded if the origin server uses the "mod" scheme, which instructs the
go command to download the module using the GOPROXY protocol.
The earliest matching pattern in the list applies, even if later patterns
might also match.

For example, consider:

	GOVCS=github.com:git,evil.com:off,*:git|hg

With this setting, code with a module or import path beginning with
github.com/ can only use git; paths on evil.com cannot use any version
control command, and all other paths (* matches everything) can use
only git or hg.

The special patterns "public" and "private" match public and private
module or import paths. A path is private if it matches the GOPRIVATE
variable; otherwise it is public.

If no rules in the GOVCS variable match a particular module or import path,
the 'go get' command applies its default rule, which can now be summarized
in GOVCS notation as 'public:git|hg,private:all'.

To allow unfettered use of any version control system for any package, use:

	GOVCS=*:all

To disable all use of version control, use:

	GOVCS=*:off

The 'go env -w' command (see 'go help env') can be used to set the GOVCS
variable for future go command invocations.
`,
}

var (
	getD        = CmdGet.Flag.Bool("d", false, "")
	getF        = CmdGet.Flag.Bool("f", false, "")
	getFix      = CmdGet.Flag.Bool("fix", false, "")
	getM        = CmdGet.Flag.Bool("m", false, "")
	getT        = CmdGet.Flag.Bool("t", false, "")
	getU        upgradeFlag
	getInsecure = CmdGet.Flag.Bool("insecure", false, "")
	// -v是cfg.BuildV
)

// upgradeFlag是一个自定义标志。值为-u。
type upgradeFlag struct {
	rawVersion string
	version    string
}

func (*upgradeFlag) IsBoolFlag() bool { return true } // 允许-u

func (v *upgradeFlag) Set(s string) error {
	if s == "false" {
		v.version = ""
		v.rawVersion = ""
	} else if s == "true" {
		v.version = "upgrade"
		v.rawVersion = ""
	} else {
		v.version = s
		v.rawVersion = s
	}
	return nil
}

func (v *upgradeFlag) String() string { return "" }

func init() {
	work.AddBuildFlags(CmdGet, work.OmitModFlag)
	CmdGet.Run = runGet // 中断初始化循环
	CmdGet.Flag.Var(&getU, "u", "")
}

func runGet(ctx context.Context, cmd *base.Command, args []string) {
	switch getU.version {
	case "", "upgrade", "patch":
		// 好啊
	default:
		base.Fatalf("go get: unknown upgrade flag -u=%s", getU.rawVersion)
	}
	if *getF {
		fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n")
	}
	if *getFix {
		fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n")
	}
	if *getM {
		base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages")
	}
	if *getInsecure {
		base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead")
	}

	// 在我们申请之前，不允许对go.mod进行任何更新
	// 所有请求的更改，并检查结果是否匹配
	// 所要求的。
	modload.DisallowWriteGoMod()

	// 允许在模块外部查找模块的导入路径。
	// 与其他命令不同，“go-get”应能完成此任务。
	modload.AllowMissingModuleImports()

	queries := parseArgs(ctx, args)

	r := newResolver(ctx, queries)
	r.performLocalQueries(ctx)
	r.performPathQueries(ctx)

	for {
		r.performWildcardQueries(ctx)
		r.performPatternAllQueries(ctx)

		if changed := r.resolveQueries(ctx, queries); changed {
			// “go-get”参数可以是（通常是）包模式，而不是
			// （只是）模块。包可以由具有前缀的任何模块提供
			// 一个通配符甚至可以匹配模块中的包
			// 完全不同的路径。因为这些影响，因为任何
			// 对所选模块版本的更改可以带来全新的功能
			// 作为依赖项的模块路径，我们需要随时重新发出查询
			// 更改生成列表。
			// None
			// 给定模块的任何版本查询的结果-即使是“升级”或
			// “修补程序”-始终与开始时的生成列表相关
			// “go get”命令不是中间状态，因此
			// DeDeDeministic，因此可计算，以及
			// 每个模块的选定版本只能在我们迭代时缩小。
			// None
			// “all”在功能上与通配符模式非常相似。一套
			// 主模块导入的包不会更改，并且查询
			// 包含每个此类包的模块的结果也不会更改
			// （在应用之前，它始终相对于初始生成列表。）
			// 查询）。因此，“全部”查询的结果可以更改的唯一方法
			// 如果某个匹配包从生成列表中的一个模块中移动
			// 对另一个人来说，这不应该经常发生。
			continue
		}

		// 加载导入时，我们会检测到以下情况：
		// None
		// -缺少需要从外部解决的可传递依赖项
		// 当前生成列表（请注意，这些列表可能会为现有的
		// 模式查询！）
		// None
		// -不匹配任何其他查询的可传递依赖项，
		// 但由于-u标志，需要升级
		// None
		// -不明确的导入错误。
		// TODO（#27899）：尝试自动解决不明确的导入错误。
		upgrades := r.findAndUpgradeImports(ctx, queries)
		if changed := r.applyUpgrades(ctx, upgrades); changed {
			continue
		}

		r.findMissingWildcards(ctx)
		if changed := r.resolveQueries(ctx, r.wildcardQueries); changed {
			continue
		}

		break
	}

	r.checkWildcardVersions(ctx)

	var pkgPatterns []string
	for _, q := range queries {
		if q.matchesPackages {
			pkgPatterns = append(pkgPatterns, q.pattern)
		}
	}
	r.checkPackageProblems(ctx, pkgPatterns)

	// 我们已经下载了模块（并确定了直接和间接
	// 依赖项）通过在FindUpgradeImports中加载包。
	// 所以如果设置了-d，我们就完成了模块的工作。
	// None
	// 否则，我们需要构建并安装与
	// 命令行参数。
	// 请注意，不带参数的“goget-u”等同于
	// “go get-u.”，因此我们通常在当前
	// 目录
	if !*getD && len(pkgPatterns) > 0 {
		work.BuildInit()

		pkgOpts := load.PackageOpts{ModResolveTests: *getT}
		var pkgs []*load.Package
		for _, pkg := range load.PackagesAndErrors(ctx, pkgOpts, pkgPatterns) {
			if pkg.Error != nil {
				var noGo *load.NoGoError
				if errors.As(pkg.Error.Err, &noGo) {
					if m := modload.PackageModule(pkg.ImportPath); m.Path == pkg.ImportPath {
						// pkg位于模块的根目录下，与当前
						// 构建标签。可能用户只是想更改的版本
						// 该模块-不同时构建包-因此抑制错误。
						// （见https:
						continue
					}
				}
			}
			pkgs = append(pkgs, pkg)
		}
		load.CheckPackageErrors(pkgs)

		haveExternalExe := false
		for _, pkg := range pkgs {
			if pkg.Name == "main" && pkg.Module != nil && pkg.Module.Path != modload.Target.Path {
				haveExternalExe = true
				break
			}
		}
		if haveExternalExe {
			fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.")
			var altMsg string
			if modload.HasModRoot() {
				altMsg = `
	To adjust and download dependencies of the current module, use 'go get -d'.
	To install using requirements of the current module, use 'go install'.
	To install ignoring the current module, use 'go install' with a version,
	like 'go install example.com/cmd@latest'.
`
			} else {
				altMsg = "\n\tUse 'go install pkg@version' instead.\n"
			}
			fmt.Fprint(os.Stderr, altMsg)
			fmt.Fprintf(os.Stderr, "\tFor more information, see https:// golang.org/doc/go-get-install不推荐\n\t运行“go-help-get”或“go-help-install”。\n）
		}

		work.InstallPackages(ctx, pkgPatterns, pkgs)
	}

	if !modload.HasModRoot() {
		return
	}

	// 一切都成功了。更新go.mod。
	oldReqs := reqsFromGoMod(modload.ModFile())

	modload.AllowWriteGoMod()
	modload.WriteGoMod(ctx)
	modload.DisallowWriteGoMod()

	newReqs := reqsFromGoMod(modload.ModFile())
	r.reportChanges(oldReqs, newReqs)
}

// parseArgs解析命令行参数并报告错误。
// None
// 命令行参数的形式如下path@version或者简单的路径，用
// 隐式@upgrade。path@none就是“降级”。
func parseArgs(ctx context.Context, rawArgs []string) []*query {
	defer base.ExitIfErrors()

	var queries []*query
	for _, arg := range search.CleanPatterns(rawArgs) {
		q, err := newQuery(arg)
		if err != nil {
			base.Errorf("go get: %v", err)
			continue
		}

		// 如果没有参数，CleanPatterns将返回“”。生
		// 请将字符串返回“”以获得更好的错误。
		if len(rawArgs) == 0 {
			q.raw = ""
		}

		// 谨防“去拿x.go”，这是一个常见的错误。
		// 请注意，包和模块路径可能以“.go”结尾，因此仅打印错误
		// 如果参数没有版本，或者没有斜杠，或者引用了现有文件。
		if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" {
			if !strings.Contains(q.raw, "/") {
				base.Errorf("go get %s: arguments must be package or module paths", q.raw)
				continue
			}
			if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() {
				base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", q.raw)
				continue
			}
		}

		queries = append(queries, q)
	}

	return queries
}

type resolver struct {
	localQueries      []*query // 查询绝对或相对路径
	pathQueries       []*query // 按原始顺序的包路径文字查询
	wildcardQueries   []*query // 按原始顺序的路径通配符查询
	patternAllQueries []*query // 模式为“all”的查询

	// 索引的“无”查询。这些也包括在上面的切片中；
	// 它们在这里被编入索引以加速非ForPath。
	nonesByPath   map[string]*query // 路径文字“@none”查询按路径索引
	wildcardNones []*query          // 通配符“@none”查询

	// resolvedVersion将每个模块路径映射到该模块的版本
	// 必须与第一个查询一起在最终生成列表中选择
	// 将模块解析为该版本（“原因”）。
	resolvedVersion map[string]versionReason

	buildList        []module.Version
	buildListVersion map[string]string // 构建列表索引（模块路径）→ （版本）

	initialVersion map[string]string // “go get”开头的初始生成列表索引

	missing []pathSet // 缺少可传递依赖项的候选项

	work *par.Queue

	matchInModuleCache par.Cache
}

type versionReason struct {
	version string
	reason  *query
}

func newResolver(ctx context.Context, queries []*query) *resolver {
	// LoadModGraph还设置modload.Target，这是各种解析器所需要的
	// 方法。
	const defaultGoVersion = ""
	mg := modload.LoadModGraph(ctx, defaultGoVersion)

	buildList := mg.BuildList()
	initialVersion := make(map[string]string, len(buildList))
	for _, m := range buildList {
		initialVersion[m.Path] = m.Version
	}

	r := &resolver{
		work:             par.NewQueue(runtime.GOMAXPROCS(0)),
		resolvedVersion:  map[string]versionReason{},
		buildList:        buildList,
		buildListVersion: initialVersion,
		initialVersion:   initialVersion,
		nonesByPath:      map[string]*query{},
	}

	for _, q := range queries {
		if q.pattern == "all" {
			r.patternAllQueries = append(r.patternAllQueries, q)
		} else if q.patternIsLocal {
			r.localQueries = append(r.localQueries, q)
		} else if q.isWildcard() {
			r.wildcardQueries = append(r.wildcardQueries, q)
		} else {
			r.pathQueries = append(r.pathQueries, q)
		}

		if q.version == "none" {
			// 索引“none”查询以提高noneForPath的效率。
			if q.isWildcard() {
				r.wildcardNones = append(r.wildcardNones, q)
			} else {
				// 相同路径的所有“<path>@none”查询都是相同的；我们只是
				// 需要索引一份。
				r.nonesByPath[q.pattern] = q
			}
		}
	}

	return r
}

// initialSelected返回具有指定路径的模块版本
// 在此“go get”调用开始时选择。
func (r *resolver) initialSelected(mPath string) (version string) {
	v, ok := r.initialVersion[mPath]
	if !ok {
		return "none"
	}
	return v
}

// selected返回具有给定路径的模块版本，该路径为
// 在冲突解决程序的当前生成列表中选择。
func (r *resolver) selected(mPath string) (version string) {
	v, ok := r.buildListVersion[mPath]
	if !ok {
		return "none"
	}
	return v
}

// noneForPath返回与给定模块路径匹配的“none”查询，
// 或者如果不存在这样的查询，则find==false。
func (r *resolver) noneForPath(mPath string) (nq *query, found bool) {
	if nq = r.nonesByPath[mPath]; nq != nil {
		return nq, true
	}
	for _, nq := range r.wildcardNones {
		if nq.matchesPath(mPath) {
			return nq, true
		}
	}
	return nil, false
}

// queryModule包装modload.Query，替换r.checkAllowedOr来决定
// 允许的版本。
func (r *resolver) queryModule(ctx context.Context, mPath, query string, selected func(string) string) (module.Version, error) {
	current := r.initialSelected(mPath)
	rev, err := modload.Query(ctx, mPath, query, current, r.checkAllowedOr(query, selected))
	if err != nil {
		return module.Version{}, err
	}
	return module.Version{Path: mPath, Version: rev.Version}, nil
}

// queryPackage包装modload.queryPackage，将r.checkAllowedOr替换为
// 决定允许的版本。
func (r *resolver) queryPackages(ctx context.Context, pattern, query string, selected func(string) string) (pkgMods []module.Version, err error) {
	results, err := modload.QueryPackages(ctx, pattern, query, selected, r.checkAllowedOr(query, selected))
	if len(results) > 0 {
		pkgMods = make([]module.Version, 0, len(results))
		for _, qr := range results {
			pkgMods = append(pkgMods, qr.Mod)
		}
	}
	return pkgMods, err
}

// queryPattern包装modload.queryPattern，将r.checkAllowedOr替换为
// 决定允许的版本。
func (r *resolver) queryPattern(ctx context.Context, pattern, query string, selected func(string) string) (pkgMods []module.Version, mod module.Version, err error) {
	results, modOnly, err := modload.QueryPattern(ctx, pattern, query, selected, r.checkAllowedOr(query, selected))
	if len(results) > 0 {
		pkgMods = make([]module.Version, 0, len(results))
		for _, qr := range results {
			pkgMods = append(pkgMods, qr.Mod)
		}
	}
	if modOnly != nil {
		mod = modOnly.Mod
	}
	return pkgMods, mod, err
}

// checkAllowedOr类似于modload.CheckAllowed，但它始终允许请求的
// 和当前版本（即使它们被收回或以其他方式排除）。
func (r *resolver) checkAllowedOr(requested string, selected func(string) string) modload.AllowedFunc {
	return func(ctx context.Context, m module.Version) error {
		if m.Version == requested {
			return modload.CheckExclusions(ctx, m)
		}
		if (requested == "upgrade" || requested == "patch") && m.Version == selected(m.Path) {
			return nil
		}
		return modload.CheckAllowed(ctx, m)
	}
}

// matchInModule是modload.matchInModule周围的缓存包装器。
func (r *resolver) matchInModule(ctx context.Context, pattern string, m module.Version) (packages []string, err error) {
	type key struct {
		pattern string
		m       module.Version
	}
	type entry struct {
		packages []string
		err      error
	}

	e := r.matchInModuleCache.Do(key{pattern, m}, func() interface{} {
		match := modload.MatchInModule(ctx, pattern, m, imports.AnyTags())
		if len(match.Errs) > 0 {
			return entry{match.Pkgs, match.Errs[0]}
		}
		return entry{match.Pkgs, nil}
	}).(entry)

	return e.packages, e.err
}

// QueryOne为每个与q模式匹配的模块向q添加候选集。
// 每个候选集只有一个可能的模块版本：匹配的
// 模块版本为“无”。
// None
// 我们首先将“go get”的参数解释为包，然后返回到
// 第二个模块。但是，版本“无”中不存在任何模块，因此
// 包也存在于该版本：我们知道参数不能匹配
// 任何包，因此它必须与模块匹配。
func (r *resolver) queryNone(ctx context.Context, q *query) {
	if search.IsMetaPackage(q.pattern) {
		panic(fmt.Sprintf("internal error: queryNone called with pattern %q", q.pattern))
	}

	if !q.isWildcard() {
		q.pathOnce(q.pattern, func() pathSet {
			if modload.HasModRoot() && q.pattern == modload.Target.Path {
				// 用户已明确请求将其自己的模块降级为
				// 版本“无”。这不是一个完全不合理的要求：它
				// 这可能意味着“降低所有依赖于任何
				// “主模块的显式版本”，或“降级到其他模块”
				// 在模块中找到与主模块路径相同的包
				// 带有主模块路径的前缀”。
				// None
				// 但是，这两种行为都不符合
				// 质询的简单含义。为了减少混乱，请拒绝
				// 显式查询。
				return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version})
			}

			return pathSet{mod: module.Version{Path: q.pattern, Version: "none"}}
		})
	}

	for _, curM := range r.buildList {
		if !q.matchesPath(curM.Path) {
			continue
		}
		q.pathOnce(curM.Path, func() pathSet {
			if modload.HasModRoot() && curM == modload.Target {
				return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version})
			}
			return pathSet{mod: module.Version{Path: curM.Path, Version: "none"}}
		})
	}
}

func (r *resolver) performLocalQueries(ctx context.Context) {
	for _, q := range r.localQueries {
		q.pathOnce(q.pattern, func() pathSet {
			absDetail := ""
			if !filepath.IsAbs(q.pattern) {
				if absPath, err := filepath.Abs(q.pattern); err == nil {
					absDetail = fmt.Sprintf(" (%s)", absPath)
				}
			}

			// 像C:\foo这样的绝对路径和像../foo这样的相对路径。。。是
			// 仅限于主模块中的匹配包。
			pkgPattern := modload.DirImportPath(ctx, q.pattern)
			if pkgPattern == "." {
				return errSet(fmt.Errorf("%s%s is not within module rooted at %s", q.pattern, absDetail, modload.ModRoot()))
			}

			match := modload.MatchInModule(ctx, pkgPattern, modload.Target, imports.AnyTags())
			if len(match.Errs) > 0 {
				return pathSet{err: match.Errs[0]}
			}

			if len(match.Pkgs) == 0 {
				if q.raw == "" || q.raw == "." {
					return errSet(fmt.Errorf("no package in current directory"))
				}
				if !q.isWildcard() {
					return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.ModRoot()))
				}
				search.WarnUnmatched([]*search.Match{match})
				return pathSet{}
			}

			return pathSet{pkgMods: []module.Version{modload.Target}}
		})
	}
}

// performWildcardQueries为每个模式为
// 是一个通配符。
// None
// 给定模块路径匹配（或包含包）的候选项
// 匹配）通配符查询仅取决于初始生成列表，而不取决于集合
// 模块的数量可能会被其他查询扩展，因此需要使用通配符查询
// 每当将可能匹配的模块路径添加到
// 构建列表。
func (r *resolver) performWildcardQueries(ctx context.Context) {
	for _, q := range r.wildcardQueries {
		q := q
		r.work.Add(func() {
			if q.version == "none" {
				r.queryNone(ctx, q)
			} else {
				r.queryWildcard(ctx, q)
			}
		})
	}
	<-r.work.Idle()
}

// queryWildcard为每个模块向q添加一个候选集，其中：
// -某些版本的模块已在生成列表中，并且
// -该模块存在于与q.version匹配的某个版本，并且
// -要么模块路径本身匹配q.pattern，要么模块路径中的某个包匹配q.pattern
// q.version处的模块与q.pattern匹配。
func (r *resolver) queryWildcard(ctx context.Context, q *query) {
	// 对于通配符模式，modload.QueryPattern仅标识模块
	// 匹配通配符之前的路径前缀。然而，构建
	// 该列表可能已经包含其他具有匹配包的模块，并且我们
	// 也应该考虑那些模块来满足查询。
	// 我们想匹配现有依赖项中的任何包，但我们只想
	// 如果没有其他问题出现，请解决新的依赖项。
	for _, curM := range r.buildList {
		if !q.canMatchInModule(curM.Path) {
			continue
		}
		q.pathOnce(curM.Path, func() pathSet {
			if _, hit := r.noneForPath(curM.Path); hit {
				// 此模块将被删除，因此它将不再位于生成列表中
				// （因此将不再与模式匹配）。
				return pathSet{}
			}

			if curM.Path == modload.Target.Path && !versionOkForMainModule(q.version) {
				if q.matchesPath(curM.Path) {
					return errSet(&modload.QueryMatchesMainModuleError{
						Pattern: q.pattern,
						Query:   q.version,
					})
				}

				packages, err := r.matchInModule(ctx, q.pattern, curM)
				if err != nil {
					return errSet(err)
				}
				if len(packages) > 0 {
					return errSet(&modload.QueryMatchesPackagesInMainModuleError{
						Pattern:  q.pattern,
						Query:    q.version,
						Packages: packages,
					})
				}

				return r.tryWildcard(ctx, q, curM)
			}

			m, err := r.queryModule(ctx, curM.Path, q.version, r.initialSelected)
			if err != nil {
				if !isNoSuchModuleVersion(err) {
					// 我们无法判断是否存在匹配的版本。
					return errSet(err)
				}
				// 没有与查询匹配的curM.Path版本。

				// 我们还没有检查curM是否包含任何匹配的包
				// 当前选择的版本，或者curM.Path本身是否与q匹配。如果
				// 这两个条件中的任何一个都适用，*和*没有其他查询更改
				// 选择curM的版本，则我们将在checkWildcardVersions中失败。
				// （这可能是个错误，但现在说还为时过早。）
				// None
				// 然而，即使这样，一些其他查询的可传递性需求
				// 可以将此模块从生成列表中完全降级，其中
				// 这种情况下，模式将不再包括它，它将不会是一个错误。
				// None
				// 不管是哪种方式，在查询上下赌注，而不是刚刚出错。
				return pathSet{}
			}

			return r.tryWildcard(ctx, q, m)
		})
	}

	// 即使没有匹配的模块，我们也不应该查询要提供的新模块
	// 模式：其他一些查询可能会导致一个新的需求
	// 将匹配通配符。相反，我们将签入findmisingwildcards。
}

// tryWildcard返回模块m匹配查询q的路径集。
// 如果m实际上与q不匹配，则tryWildcard返回一个空路径集。
func (r *resolver) tryWildcard(ctx context.Context, q *query, m module.Version) pathSet {
	mMatches := q.matchesPath(m.Path)
	packages, err := r.matchInModule(ctx, q.pattern, m)
	if err != nil {
		return errSet(err)
	}
	if len(packages) > 0 {
		return pathSet{pkgMods: []module.Version{m}}
	}
	if mMatches {
		return pathSet{mod: m}
	}
	return pathSet{}
}

// findMissingWildcards为r.wildcardQueries中的每个查询添加候选集
// 尚未解析为包含包的任何版本。
func (r *resolver) findMissingWildcards(ctx context.Context) {
	for _, q := range r.wildcardQueries {
		if q.version == "none" || q.matchesPackages {
			continue // q不是“缺失”
		}
		r.work.Add(func() {
			q.pathOnce(q.pattern, func() pathSet {
				pkgMods, mod, err := r.queryPattern(ctx, q.pattern, q.version, r.initialSelected)
				if err != nil {
					if isNoSuchPackageVersion(err) && len(q.resolved) > 0 {
						// q已解析一个或多个模块，但不匹配任何包。
						// 没关系：这个模式只是一个模块模式，我们没有
						// 需要添加更多的模块来满足它。
						return pathSet{}
					}
					return errSet(err)
				}

				return pathSet{pkgMods: pkgMods, mod: mod}
			})
		})
	}
	<-r.work.Idle()
}

// 如果生成列表中的任何模块具有
// 使用通配符模式匹配查询的路径（或包含包），但
// 所选版本与查询*不*匹配。
func (r *resolver) checkWildcardVersions(ctx context.Context) {
	defer base.ExitIfErrors()

	for _, q := range r.wildcardQueries {
		for _, curM := range r.buildList {
			if !q.canMatchInModule(curM.Path) {
				continue
			}
			if !q.matchesPath(curM.Path) {
				packages, err := r.matchInModule(ctx, q.pattern, curM)
				if len(packages) == 0 {
					if err != nil {
						reportError(q, err)
					}
					continue // curM与q无关。
				}
			}

			rev, err := r.queryModule(ctx, curM.Path, q.version, r.initialSelected)
			if err != nil {
				reportError(q, err)
				continue
			}
			if rev.Version == curM.Version {
				continue // curM已经匹配了q。
			}

			if !q.matchesPath(curM.Path) {
				m := module.Version{Path: curM.Path, Version: rev.Version}
				packages, err := r.matchInModule(ctx, q.pattern, m)
				if err != nil {
					reportError(q, err)
					continue
				}
				if len(packages) == 0 {
					// curM在其原始版本中包含与q模式匹配的路径，
					// 但在rev.版本中，它并没有，所以（有些自相矛盾）如果
					// 我们更改了curM的版本，它将不再匹配查询。
					var version interface{} = m
					if rev.Version != q.version {
						version = fmt.Sprintf("%s@%s (%s)", m.Path, q.version, m.Version)
					}
					reportError(q, fmt.Errorf("%v matches packages in %v but not %v: specify a different version for module %s", q, curM, version, m.Path))
					continue
				}
			}

			// 因为queryModule成功了，curM或其中一个包
			// 包含匹配的q.pattern，我们应该选择
			// 与q匹配的curM，或报告冲突错误（并退出）。
			// 如果我们还在这里，版本不匹配，
			// 出了大问题。
			reportError(q, fmt.Errorf("internal error: selected %v instead of %v", curM, rev.Version))
		}
	}
}

// performPathQueries填充模式为
// 路径文字。
// None
// 路径文本的候选包和模块仅取决于
// 初始构建列表，而不是当前构建列表，因此我们只需要查询路径
// 文字一次。
func (r *resolver) performPathQueries(ctx context.Context) {
	for _, q := range r.pathQueries {
		q := q
		r.work.Add(func() {
			if q.version == "none" {
				r.queryNone(ctx, q)
			} else {
				r.queryPath(ctx, q)
			}
		})
	}
	<-r.work.Idle()
}

// queryPath为路径为q.pattern的包将候选集添加到q。
// 候选集由能够提供q模式的所有模块组成
// 并且有一个与q匹配的版本，加上（如果存在）其路径为
// 本身就是q.pattern（在匹配的版本中）。
func (r *resolver) queryPath(ctx context.Context, q *query) {
	q.pathOnce(q.pattern, func() pathSet {
		if search.IsMetaPackage(q.pattern) || q.isWildcard() {
			panic(fmt.Sprintf("internal error: queryPath called with pattern %q", q.pattern))
		}
		if q.version == "none" {
			panic(`internal error: queryPath called with version "none"`)
		}

		if search.IsStandardImportPath(q.pattern) {
			stdOnly := module.Version{}
			packages, _ := r.matchInModule(ctx, q.pattern, stdOnly)
			if len(packages) > 0 {
				if q.rawVersion != "" {
					return errSet(fmt.Errorf("can't request explicit version %q of standard library package %s", q.version, q.pattern))
				}

				q.matchesPackages = true
				return pathSet{} // 标准库不需要模块。
			}
		}

		pkgMods, mod, err := r.queryPattern(ctx, q.pattern, q.version, r.initialSelected)
		if err != nil {
			return errSet(err)
		}
		return pathSet{pkgMods: pkgMods, mod: mod}
	})
}

// performPatternAllQueries为其
// 模式是“全部”。
// None
// “all”中给定包的候选模块仅取决于初始值
// 构建列表，但我们无法跟踪给定包的依赖项，直到
// 知道选择了哪一位候选人，而这一选择可能取决于
// 其他查询的结果。我们需要随时重新评估“所有”查询
// 解析“全部”中一个或多个包的模块。
func (r *resolver) performPatternAllQueries(ctx context.Context) {
	if len(r.patternAllQueries) == 0 {
		return
	}

	findPackage := func(ctx context.Context, path string, m module.Version) (versionOk bool) {
		versionOk = true
		for _, q := range r.patternAllQueries {
			q.pathOnce(path, func() pathSet {
				pkgMods, err := r.queryPackages(ctx, path, q.version, r.initialSelected)
				if len(pkgMods) != 1 || pkgMods[0] != m {
					// 对于给定的路径，除了m之外还有其他候选项，因此我们不能
					// 确保m实际上是所选择提供的模块
					// 包裹。暂时不要加载它的依赖项，因为它们
					// 解决正确的问题后，可能不再是依赖项
					// 版本
					versionOk = false
				}
				return pathSet{pkgMods: pkgMods, err: err}
			})
		}
		return versionOk
	}

	r.loadPackages(ctx, []string{"all"}, findPackage)

	// 由于我们同时建立了候选人名单，他们可能在一个
	// 不确定的顺序。我们希望“go-get”是完全确定的，
	// 包括它选择报告的错误，所以对候选项进行排序
	// 进入一个确定但任意的顺序。
	for _, q := range r.patternAllQueries {
		sort.Slice(q.candidates, func(i, j int) bool {
			return q.candidates[i].path < q.candidates[j].path
		})
	}
}

// FindUpgradeImports为每个尚未导入的包返回路径集
// 在生成列表中，但由与
// 给定的查询（必须已经解决）。
// None
// 如果设置了getU标志（“-u”），FindUpgradeImports也会返回一个
// 不受任何其他模块约束的每个模块的路径集
// 命令行参数，并具有可用的匹配升级。
func (r *resolver) findAndUpgradeImports(ctx context.Context, queries []*query) (upgrades []pathSet) {
	patterns := make([]string, 0, len(queries))
	for _, q := range queries {
		if q.matchesPackages {
			patterns = append(patterns, q.pattern)
		}
	}
	if len(patterns) == 0 {
		return nil
	}

	// mu保护对升级的并发写入，升级将被排序
	// （恢复确定性）加载后。
	var mu sync.Mutex

	findPackage := func(ctx context.Context, path string, m module.Version) (versionOk bool) {
		version := "latest"
		if m.Path != "" {
			if getU.version == "" {
				// 用户没有请求我们升级可传递依赖项。
				return true
			}
			if _, ok := r.resolvedVersion[m.Path]; ok {
				// 我们无法隐式升级m，因为它的版本由
				// 一个明确的模式参数。
				return true
			}
			version = getU.version
		}

		// 与其他查询不同，“-u”标志相对于生成列表进行升级
		// 在应用到目前为止的更改后，不是初始生成列表。
		// 这有两个原因：
		// None
		// -“-u”标志有意应用于可传递依赖项，
		// 在申请前可能不知道或甚至无法解决的问题
		// 其他版本的更改。
		// None
		// -与其他参数不同，“-u”标志不会导致版本更改
		// 与其他查询冲突。（另一个查询始终获胜。）

		pkgMods, err := r.queryPackages(ctx, path, version, r.selected)
		for _, u := range pkgMods {
			if u == m {
				// 所选软件包版本已适当升级；那里
				// 没有必要改变它。
				return true
			}
		}

		if err != nil {
			if isNoSuchPackageVersion(err) || (m.Path == "" && module.CheckPath(path) != nil) {
				// 我们找不到包，因为它根本不存在
				// 在最新版本的任何模块中。（请注意，模块路径无效
				// 由于替换，可能会普遍存在，所以我们至少需要
				// 运行查询以检查这些属性。）
				// None
				// 我们无法更改版本来修复软件包，所以请离开
				// 这件事没有解决。或者是其他查询（可能是与
				// 新添加的依赖项（其他缺少的包）将填充
				// 否则，我们将在中报告错误（具有更好的导入堆栈）
				// 最后的LoadPackages调用。
				return true
			}
		}

		mu.Lock()
		upgrades = append(upgrades, pathSet{path: path, pkgMods: pkgMods, err: err})
		mu.Unlock()
		return false
	}

	r.loadPackages(ctx, patterns, findPackage)

	// 由于我们同时建立了候选人名单，他们可能在一个
	// 不确定的顺序。我们希望“go-get”是完全确定的，
	// 包括它选择报告的错误，所以对候选项进行排序
	// 进入一个确定但任意的顺序。
	sort.Slice(upgrades, func(i, j int) bool {
		return upgrades[i].path < upgrades[j].path
	})
	return upgrades
}

// loadPackages加载与给定模式匹配的包，调用
// findPackage函数，用于可能需要更改
// 构建列表。
// None
// loadPackages为从中加载的每个包调用findPackage函数
// 主模块外部的模块。如果提供该功能的模块或版本
// 由于查询需要更改包，findPackage可能返回false
// 并且不会加载该包的导入。
// None
// loadPackages还为每个导入的包调用findPackage函数
// 这既不存在于标准库中，也不存在于
// 构建列表。
func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPackage func(ctx context.Context, path string, m module.Version) (versionOk bool)) {
	opts := modload.PackageOpts{
		Tags:                     imports.AnyTags(),
		VendorModulesInGOROOTSrc: true,
		LoadTests:                *getT,
		AssumeRootsImported:      true, // 在“go get foo”之后，应该构建foo的导入。
		SilencePackageErrors:     true, // 可通过后续升级或降级进行修复。
	}

	opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error {
		if m.Path == "" || m == modload.Target {
			// 标准库和主模块中的软件包已处于其初始状态
			// 最新（且仅限）可用版本。
			return nil
		}
		if ok := findPackage(ctx, path, m); !ok {
			return errVersionChange
		}
		return nil
	}

	_, pkgs := modload.LoadPackages(ctx, opts, patterns...)
	for _, path := range pkgs {
		const (
			parentPath  = ""
			parentIsStd = false
		)
		_, _, err := modload.Lookup(parentPath, parentIsStd, path)
		if err == nil {
			continue
		}
		if errors.Is(err, errVersionChange) {
			// 我们已经在加载过程中添加了候选项。
			continue
		}

		var (
			importMissing *modload.ImportMissingError
			ambiguous     *modload.AmbiguousImportError
		)
		if !errors.As(err, &importMissing) && !errors.As(err, &ambiguous) {
			// 这个包是我们关心的东西的依赖项，它有一些
			// 无法通过版本更改解决的问题。
			// 将错误留给最后的LoadPackages调用。
			continue
		}

		path := path
		r.work.Add(func() {
			findPackage(ctx, path, module.Version{})
		})
	}
	<-r.work.Idle()
}

// errVersionChange是一个sentinel错误，指示模块的版本需要更改
// 要在加载其依赖项之前更新。
var errVersionChange = errors.New("version change needed")

// resolveQueries解析附加到给定对象的候选集
// 查询和/或需要提供给定缺少的包依赖项。
// None
// resolveQueries首先从每个模块中解析一个模块版本
// 附加到给定查询的明确路径集。
// None
// 如果没有明确的查询导致生成列表的更改，
// resolveQueries重新访问不明确的候选查询并解析它们
// 为了保证前进而武断。
// None
// 如果解析了所有路径集，而未对生成列表进行任何更改，
// resolveQueries返回changed=false。
func (r *resolver) resolveQueries(ctx context.Context, queries []*query) (changed bool) {
	defer base.ExitIfErrors()

	// None
	// None
	// 如果我们对路径集进行索引，我们也许可以将其归结为O（N）
	// 按模块路径，这样我们在
	// 已确定其包含包列表中某些模块的版本。
	// None
	// 然而，N往往很小，大多数候选集只包含一个
	// 候选模块（因此它们将在第一次迭代中得到解决），因此
	// None

	resolved := 0
	for {
		prevResolved := resolved

		for _, q := range queries {
			unresolved := q.candidates[:0]

			for _, cs := range q.candidates {
				if cs.err != nil {
					reportError(q, cs.err)
					resolved++
					continue
				}

				filtered, isPackage, m, unique := r.disambiguate(cs)
				if !unique {
					unresolved = append(unresolved, filtered)
					continue
				}

				if m.Path == "" {
					// 这个查询是不可行的。从中选择任意候选人
					// 在筛选和“解决”冲突之前，请先报告冲突。
					isPackage, m = r.chooseArbitrarily(cs)
				}
				if isPackage {
					q.matchesPackages = true
				}
				r.resolve(q, m)
				resolved++
			}

			q.candidates = unresolved
		}

		base.ExitIfErrors()
		if resolved == prevResolved {
			break // 没有明确的候选人留下来。
		}
	}

	if resolved > 0 {
		if changed = r.updateBuildList(ctx, nil); changed {
			// 生成列表已更改，因此忽略任何剩余的不明确查询：
			// 它们现在可能由构建列表中的需求来确定，而我们
			// 希望使用而不是任意版本。
			return true
		}
	}

	// 构建列表在下一次迭代中将与在本次迭代中相同
	// 迭代，所以任何模棱两可的查询都将保持不变。为了使
	// 进步，任意但果断地解决它们。
	// None
	// 如果这导致版本冲突，用户可以重新运行“go get”
	// 具有冲突包的其他显式版本，或
	// 模块。
	resolvedArbitrarily := 0
	for _, q := range queries {
		for _, cs := range q.candidates {
			isPackage, m := r.chooseArbitrarily(cs)
			if isPackage {
				q.matchesPackages = true
			}
			r.resolve(q, m)
			resolvedArbitrarily++
		}
	}
	if resolvedArbitrarily > 0 {
		changed = r.updateBuildList(ctx, nil)
	}
	return changed
}

// applyUpgrades消除了升级（或
// 提供）以前解析的包导入的可传递依赖项。
// None
// applyUpgrades通过从每个模块添加一个模块版本来修改构建列表
// 在升级中设置路径，然后根据需要降级（或进一步升级）这些模块
// 需要维护其他模块的任何已解析版本。
// applyUpgrades不会将新版本标记为已解决，因此它们仍然可以
// 可由其他查询（如通配符）进一步修改。
// None
// 如果解析了所有路径集，而未对生成列表进行任何更改，
// applyUpgrades返回changed=false。
func (r *resolver) applyUpgrades(ctx context.Context, upgrades []pathSet) (changed bool) {
	defer base.ExitIfErrors()

	// 任意添加一个“最新”版本，提供每个缺少的包，但是
	// 不要将版本标记为已解决：我们仍然希望允许显式
	// 查询以修改生成的版本。
	var tentative []module.Version
	for _, cs := range upgrades {
		if cs.err != nil {
			base.Errorf("go get: %v", cs.err)
			continue
		}

		filtered, _, m, unique := r.disambiguate(cs)
		if !unique {
			_, m = r.chooseArbitrarily(filtered)
		}
		if m.Path == "" {
			// 缺少的一揽子计划没有可行的候选人。
			// 让它悬而未决。
			continue
		}
		tentative = append(tentative, m)
	}
	base.ExitIfErrors()

	changed = r.updateBuildList(ctx, tentative)
	return changed
}

// 消除歧义消除cs中与其他模块冲突的候选项
// 已解析的版本。如果只有一个（唯一）
// 剩余的候选者，消歧返回该候选者，以及
// 指示该结果是否将cs.path解释为包
// None
// 注意：我们在这里只做非常简单的消歧。目标是
// 再现用户的意图，而不是找到人类无法找到的解决方案。
// 在绝大多数情况下，我们期望每个路径集只有一个模块，
// 但我们希望提供一些最小的附加工具，以便用户可以添加
// 在命令行上增加一两个参数，以解决简单的歧义。
func (r *resolver) disambiguate(cs pathSet) (filtered pathSet, isPackage bool, m module.Version, unique bool) {
	if len(cs.pkgMods) == 0 && cs.mod.Path == "" {
		panic("internal error: resolveIfUnambiguous called with empty pathSet")
	}

	for _, m := range cs.pkgMods {
		if _, ok := r.noneForPath(m.Path); ok {
			// 版本为“none”的查询将强制候选模块更新版本
			// “无”，因此我们不能为该模块使用任何其他版本。
			continue
		}

		if m.Path == modload.Target.Path {
			if m.Version == modload.Target.Version {
				return pathSet{}, true, m, true
			}
			// 主模块只能设置为自己的版本。
			continue
		}

		vr, ok := r.resolvedVersion[m.Path]
		if !ok {
			// m是这个问题的可行答案，但其他答案也可能是
			// 仍然是可行的。
			filtered.pkgMods = append(filtered.pkgMods, m)
			continue
		}

		if vr.version != m.Version {
			// 某些查询强制候选模块使用除此之外的版本
			// 一
			// None
			// 该命令可能类似于
			// None
			// 去获取example.com/foo/bar@noneexample.com/foo/bar/baz@latest
			// None
			// 在这种情况下，我们*无法*从解析包
			// example.com/foo/bar（因为它受版本限制
			// “无”），并且必须通过module example.com/foo@latest.
			continue
		}

		// 一些查询强制候选模块*到*候选版本。
		// 因此，该候选人是唯一可行的选择
		// 其包：任何其他选择都会导致不明确的导入
		// 为了这条路。
		// None
		// 例如，考虑命令
		// None
		// 去获取example.com/foo@latestexample.com/foo/bar/baz@latest
		// None
		// 如果模块example.com/foo和example.com/foo/bar都提供
		// package example.com/foo/bar/baz，那么我们*必须*解析该包
		// from example.com/foo：如果我们从
		// example.com/foo/bar，我们将有两份包的副本。
		return pathSet{}, true, m, true
	}

	if cs.mod.Path != "" {
		vr, ok := r.resolvedVersion[cs.mod.Path]
		if !ok || vr.version == cs.mod.Version {
			filtered.mod = cs.mod
		}
	}

	if len(filtered.pkgMods) == 1 &&
		(filtered.mod.Path == "" || filtered.mod == filtered.pkgMods[0]) {
		// 只有一个可行的模块包含具有给定路径的包
		// （到目前为止，这是常见的情况），因此我们可以毫不含糊地解决它。
		return pathSet{}, true, filtered.pkgMods[0], true
	}

	if len(filtered.pkgMods) == 0 {
		// 可能作为包提供路径的所有模块与其他模块冲突
		// 已解决的争论。如果它可以引用模块，则返回该模块；
		// 否则，无法解析此路径集（我们将返回
		// 零模块。版本）。
		return pathSet{}, false, filtered.mod, true
	}

	// 查询仍然不明确：至少有两个不同的模块
	// cs.path可以引用的。
	return filtered, false, module.Version{}, false
}

// ChooseArBitrary返回任意（但确定性）模块版本
// 从给定集合中的那些。
// None
// ChooseArBitrary首选已在上的生成列表中的模块路径
// “go-get”的开头更喜欢提供包的模块，而不是那些提供包的模块
// 不要，并选择符合这些标准的第一个模块（因此偏向于
// 更长的路径）。
func (r *resolver) chooseArbitrarily(cs pathSet) (isPackage bool, m module.Version) {
	// 希望升级已在生成列表中的某些模块。
	for _, m := range cs.pkgMods {
		if r.initialSelected(m.Path) != "none" {
			return true, m
		}
	}

	// 否则，任意选择提供包的第一个模块。
	if len(cs.pkgMods) > 0 {
		return true, cs.pkgMods[0]
	}

	return false, cs.mod
}

// checkPackageProblems重新加载给定模式和报告的包
// 缺少和不明确的包错误。它还报告了撤回和撤销
// 已解析模块和生成命名包所需模块的弃用。
// 它还为构建列表中的每个更新模块（如果有）添加一个总和
// 之前，在加载包时没有得到一个。
// None
// 我们在过程的前面跳过丢失的包错误，因为我们希望
// 我们自己解决路径集，但在这一点上，我们没有足够的上下文
// 记录导致每个错误的包导入链。
func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []string) {
	defer base.ExitIfErrors()

	// 收集有关我们可能要加载收回和
	// 反对。加载此元数据至少需要一个版本
	// 查找每个模块，我们不想加载既不是也不是的信息
	// 相关的或不可采取行动的。
	type modFlags int
	const (
		resolved modFlags = 1 << iota // 由“go get”解析的版本
		named                         // 在命令行上显式命名或提供命名包
		hasPkg                        // 需要构建命名包
		direct                        // 提供主模块的直接依赖关系
	)
	relevantMods := make(map[module.Version]modFlags)
	for path, reason := range r.resolvedVersion {
		m := module.Version{Path: path, Version: reason.version}
		relevantMods[m] |= resolved
	}

	// 重新加载包，报告丢失和不明确导入的错误。
	if len(pkgPatterns) > 0 {
		// LoadPackages将打印错误（因为它有更多的上下文），但不会打印错误
		// 退出，因为我们需要稍后加载收回。
		pkgOpts := modload.PackageOpts{
			VendorModulesInGOROOTSrc: true,
			LoadTests:                *getT,
			ResolveMissingImports:    false,
			AllowErrors:              true,
			SilenceNoGoErrors:        true,
		}
		matches, pkgs := modload.LoadPackages(ctx, pkgOpts, pkgPatterns...)
		for _, m := range matches {
			if len(m.Errs) > 0 {
				base.SetExitStatus(1)
				break
			}
		}
		for _, pkg := range pkgs {
			if dir, _, err := modload.Lookup("", false, pkg); err != nil {
				if dir != "" && errors.Is(err, imports.ErrNoGo) {
					// 因为dir是非空的，所以我们必须找到源文件
					// 与包或其测试关联-ErrNoGo必须
					// 指示这些源文件中没有一个碰巧应用于此
					// 配置如果我们真的在构建包（no-d
					// 我们会在那时报告问题；否则，假设
					// 用户将在其他应用程序中构建或测试此包
					// 配置并抑制错误。
					continue
				}

				base.SetExitStatus(1)
				if ambiguousErr := (*modload.AmbiguousImportError)(nil); errors.As(err, &ambiguousErr) {
					for _, m := range ambiguousErr.Modules {
						relevantMods[m] |= hasPkg
					}
				}
			}
			if m := modload.PackageModule(pkg); m.Path != "" {
				relevantMods[m] |= hasPkg
			}
		}
		for _, match := range matches {
			for _, pkg := range match.Pkgs {
				m := modload.PackageModule(pkg)
				relevantMods[m] |= named
			}
		}
	}

	reqs := modload.LoadModFile(ctx)
	for m := range relevantMods {
		if reqs.IsDirect(m.Path) {
			relevantMods[m] |= direct
		}
	}

	// 命令行和模块中提到的模块的装载收回
	// 需要构建命名包。我们关心间接合同的撤回
	// 依赖项，因为我们可能能够从它们中升级。
	type modMessage struct {
		m       module.Version
		message string
	}
	retractions := make([]modMessage, 0, len(relevantMods))
	for m, flags := range relevantMods {
		if flags&(resolved|named|hasPkg) != 0 {
			retractions = append(retractions, modMessage{m: m})
		}
	}
	sort.Slice(retractions, func(i, j int) bool { return retractions[i].m.Path < retractions[j].m.Path })
	for i := range retractions {
		i := i
		r.work.Add(func() {
			err := modload.CheckRetractions(ctx, retractions[i].m)
			if retractErr := (*modload.ModuleRetractedError)(nil); errors.As(err, &retractErr) {
				retractions[i].message = err.Error()
			}
		})
	}

	// 加载命令行中提到的模块的弃用。仅装载
	// 如果间接依赖项也是直接依赖项，则不推荐使用
	// 主模块的设计。对纯间接依赖的反对意见如下：
	// 不可起诉。
	deprecations := make([]modMessage, 0, len(relevantMods))
	for m, flags := range relevantMods {
		if flags&(resolved|named) != 0 || flags&(hasPkg|direct) == hasPkg|direct {
			deprecations = append(deprecations, modMessage{m: m})
		}
	}
	sort.Slice(deprecations, func(i, j int) bool { return deprecations[i].m.Path < deprecations[j].m.Path })
	for i := range deprecations {
		i := i
		r.work.Add(func() {
			deprecation, err := modload.CheckDeprecation(ctx, deprecations[i].m)
			if err != nil || deprecation == "" {
				return
			}
			deprecations[i].message = modload.ShortMessage(deprecation, "")
		})
	}

	// 加载以前有和的更新模块的和。当我们更新
	// 模块，我们可以更新生成列表中提供
	// “all”中的包，该包未作为此“go get”命令的一部分加载。
	// 如果我们不为该模块添加一个总和，构建可能会在以后失败。
	// 请注意，意外更新的包仍然可以导入包
	// 来自未知模块或来自构建列表中我们没有的模块
	// 以前需要。我们不能在不加载“全部”的情况下处理那个案子。
	sumErrs := make([]error, len(r.buildList))
	for i := range r.buildList {
		i := i
		m := r.buildList[i]
		mActual := m
		if mRepl := modload.Replacement(m); mRepl.Path != "" {
			mActual = mRepl
		}
		old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}
		if old.Version == "" {
			continue
		}
		oldActual := old
		if oldRepl := modload.Replacement(old); oldRepl.Path != "" {
			oldActual = oldRepl
		}
		if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(oldActual) {
			continue
		}
		r.work.Add(func() {
			if _, err := modfetch.DownloadZip(ctx, mActual); err != nil {
				verb := "upgraded"
				if semver.Compare(m.Version, old.Version) < 0 {
					verb = "downgraded"
				}
				replaced := ""
				if mActual != m {
					replaced = fmt.Sprintf(" (replaced by %s)", mActual)
				}
				err = fmt.Errorf("%s %s %s => %s%s: error finding sum for %s: %v", verb, m.Path, old.Version, m.Version, replaced, mActual, err)
				sumErrs[i] = err
			}
		})
	}

	<-r.work.Idle()

	// 报告弃用，然后是收回，然后是取和错误。
	// 只有获取和的错误才是硬错误。
	for _, mm := range deprecations {
		if mm.message != "" {
			fmt.Fprintf(os.Stderr, "go: module %s is deprecated: %s\n", mm.m.Path, mm.message)
		}
	}
	var retractPath string
	for _, mm := range retractions {
		if mm.message != "" {
			fmt.Fprintf(os.Stderr, "go: warning: %v\n", mm.message)
			if retractPath == "" {
				retractPath = mm.m.Path
			} else {
				retractPath = "<module>"
			}
		}
	}
	if retractPath != "" {
		fmt.Fprintf(os.Stderr, "go: to switch to the latest unretracted version, run:\n\tgo get %s@latest\n", retractPath)
	}
	for _, err := range sumErrs {
		if err != nil {
			base.Errorf("go: %v", err)
		}
	}
	base.ExitIfErrors()
}

// reportChanges将版本更改记录到os.Stderr。
// None
// reportChanges仅记录对命令行上命名的模块和
// go.mod中明确要求的模块。对间接需求的大多数更改
// 与用户无关且未记录。
// None
// reportChanges应在WriteGoMod之后调用。
func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) {
	type change struct {
		path, old, new string
	}
	changes := make(map[string]change)

	// 收集与命令行参数匹配的模块中的更改。
	for path, reason := range r.resolvedVersion {
		old := r.initialVersion[path]
		new := reason.version
		if old != new && (old != "" || new != "none") {
			changes[path] = change{path, old, new}
		}
	}

	// 收集对go.mod中明确需求的更改。
	for _, req := range oldReqs {
		path := req.Path
		old := req.Version
		new := r.buildListVersion[path]
		if old != new {
			changes[path] = change{path, old, new}
		}
	}
	for _, req := range newReqs {
		path := req.Path
		old := r.initialVersion[path]
		new := req.Version
		if old != new {
			changes[path] = change{path, old, new}
		}
	}

	sortedChanges := make([]change, 0, len(changes))
	for _, c := range changes {
		sortedChanges = append(sortedChanges, c)
	}
	sort.Slice(sortedChanges, func(i, j int) bool {
		return sortedChanges[i].path < sortedChanges[j].path
	})
	for _, c := range sortedChanges {
		if c.old == "" {
			fmt.Fprintf(os.Stderr, "go get: added %s %s\n", c.path, c.new)
		} else if c.new == "none" || c.new == "" {
			fmt.Fprintf(os.Stderr, "go get: removed %s %s\n", c.path, c.old)
		} else if semver.Compare(c.new, c.old) > 0 {
			fmt.Fprintf(os.Stderr, "go get: upgraded %s %s => %s\n", c.path, c.old, c.new)
		} else {
			fmt.Fprintf(os.Stderr, "go get: downgraded %s %s => %s\n", c.path, c.old, c.new)
		}
	}

	// TODO（golang.org/issue/33284）：命令行参数的属性更改。
	// 对于与命令行参数匹配的模块，这可能不是
	// 这是必要的，但对于
	// 主模块。
}

// 解析模块m必须处于其指定版本的记录（可能是
// “无”）由于查询q。如果其他查询强制模块m处于
// 不同版本的“解决”报告冲突错误。
func (r *resolver) resolve(q *query, m module.Version) {
	if m.Path == "" {
		panic("internal error: resolving a module.Version with an empty path")
	}

	if m.Path == modload.Target.Path && m.Version != modload.Target.Version {
		reportError(q, &modload.QueryMatchesMainModuleError{
			Pattern: q.pattern,
			Query:   q.version,
		})
		return
	}

	vr, ok := r.resolvedVersion[m.Path]
	if ok && vr.version != m.Version {
		reportConflict(q, m, vr)
		return
	}
	r.resolvedVersion[m.Path] = versionReason{m.Version, q}
	q.resolved = append(q.resolved, m)
}

// updateBuildList将模块加载程序的全局生成列表更新为
// 与r.resolvedVersion一致，并包括附加模块
// 前提是它们与已解决的版本不冲突。
// None
// 如果附加模块与已解决的版本冲突，它们将被删除
// 降级为非冲突版本（可能为“无”）。
// None
// 如果生成的生成列表与上次生成的列表相同
// 调用updateBuildList，updateBuildList返回changed=false。
func (r *resolver) updateBuildList(ctx context.Context, additions []module.Version) (changed bool) {
	defer base.ExitIfErrors()

	resolved := make([]module.Version, 0, len(r.resolvedVersion))
	for mPath, rv := range r.resolvedVersion {
		if mPath != modload.Target.Path {
			resolved = append(resolved, module.Version{Path: mPath, Version: rv.version})
		}
	}

	changed, err := modload.EditBuildList(ctx, additions, resolved)
	if err != nil {
		var constraint *modload.ConstraintError
		if !errors.As(err, &constraint) {
			base.Errorf("go get: %v", err)
			return false
		}

		reason := func(m module.Version) string {
			rv, ok := r.resolvedVersion[m.Path]
			if !ok {
				panic(fmt.Sprintf("internal error: can't find reason for requirement on %v", m))
			}
			return rv.reason.ResolvedString(module.Version{Path: m.Path, Version: rv.version})
		}
		for _, c := range constraint.Conflicts {
			base.Errorf("go get: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint))
		}
		return false
	}
	if !changed {
		return false
	}

	const defaultGoVersion = ""
	r.buildList = modload.LoadModGraph(ctx, defaultGoVersion).BuildList()
	r.buildListVersion = make(map[string]string, len(r.buildList))
	for _, m := range r.buildList {
		r.buildListVersion[m.Path] = m.Version
	}
	return true
}

func reqsFromGoMod(f *modfile.File) []module.Version {
	reqs := make([]module.Version, len(f.Require))
	for i, r := range f.Require {
		reqs[i] = r.Mod
	}
	return reqs
}

// isNoSuchModuleVersion报告err是否表示请求的模块
// 在请求的版本中不存在，或者是因为模块不存在
// 根本不存在，或者因为它不包括该特定版本。
func isNoSuchModuleVersion(err error) bool {
	var noMatch *modload.NoMatchingVersionError
	return errors.Is(err, os.ErrNotExist) || errors.As(err, &noMatch)
}

// isNoSuchPackageVersion报告err是否表示请求的
// 由于没有模块，请求的版本中不存在包
// 它可能包含在该版本中，或者因为每个这样的模块
// 它不存在，但实际上并不包含该包。
func isNoSuchPackageVersion(err error) bool {
	var noPackage *modload.PackageNotInModuleError
	return isNoSuchModuleVersion(err) || errors.As(err, &noPackage)
}
